package org.springframework.beans.factory.support;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.TypeUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.*;
import org.springframework.core.convert.ConversionService;

import java.lang.reflect.Method;

/**
 * 抽象的实现自动装配的bean工厂
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {

    //定义默认的实例化策略
    private InstantiationStrategy instantiationStrategy = new SimpleInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException {
        //如果bean需要代理，则直接返回代理对象
        //但是无论如何改返回的bean一定是null啊
        Object bean = resolveBeforeInstantiation(beanName, beanDefinition);
        if (bean != null) {
            return bean;
        }

        //所以一定会调用创建普通bean的方法
        return doCreateBean(beanName, beanDefinition);
    }


    /**
     * 执行InstantiationAwareBeanPostProcessor的方法，如果bean需要代理，直接返回代理对象
     *
     * @param beanName
     * @param beanDefinition
     * @return
     */
    protected Object resolveBeforeInstantiation(String beanName, BeanDefinition beanDefinition) {
        //这里bean的返回结果一定是null
        Object bean = applyBeanPostProcessorsBeforeInstantiation(beanDefinition.getBeanClass(), beanName);
        if (bean != null) {
            bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
        }
        return bean;
    }

    /**
     * 执行生成代理bean的调用
     * 但是返回结果一定是null
     *
     * @param beanClass
     * @param beanName
     * @return
     */
    protected Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass, String beanName) {
        for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
            if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
                //返回的result一定是null 因为后处理器的实例化后的前处理方法 返回值是写死的null
                Object result = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessBeforeInstantiation(beanClass, beanName);
                if (result != null) {
                    return result;
                }
            }
        }

        return null;
    }


    /**
     * 实际执行创建bean的逻辑代码
     * @param beanName
     * @param beanDefinition
     * @return
     */
    private Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
        Object bean;
        try {
            //实例化bean
            bean = createBeanInstance(beanDefinition);

            //为解决循环依赖问题，将实例化后的bean 也就是赋值属性之前的不完整的bean
            //放进三级缓存中提前暴露
            if (beanDefinition.isSingleton()) {
                //如果bean是单例的才放入缓存中
                Object finalBean = bean;
                //将bean添加到三级缓存里
                addSingletonFactory(beanName, new ObjectFactory<Object>() {
                    //这个方法会判断是返回普通bean还是代理bean
                    @Override
                    public Object getObject() throws BeansException {
                        return getEarlyBeanReference(beanName, beanDefinition, finalBean);
                    }
                });
            }

            //实例化bean之后执行
            boolean continueWithPropertyPopulation = applyBeanPostProcessorsAfterInstantiation(beanName, bean);
            if (!continueWithPropertyPopulation) {
                return bean;
            }

            //在设置bean属性之前，允许BeanPostProcessor修改属性值 给bean添加@Value注解的属性
            applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition);
            //实例化完bean之后 还要通过反射给bean添加属性
            applyPropertyValues(beanName, bean, beanDefinition);
            //填充完属性要执行初始化方法，在初始化方法的前后要执行扩展方法 在这个方法里面 初始化方法后的后处理器方法 才会生成代理对象
            bean = initializeBean(beanName, bean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }

        //注册有销毁方法的bean
        registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);

        //只有单例bean才要放到单例池里长期存储 而prototype的bean 每次创建都是不一样的bean
        if (beanDefinition.isSingleton()) {
            //如果有代理对象，此处获取代理对象 再存放到单例池里。。
            //感觉多此一举 实则不然 因为创建代理对象的逻辑有修改
            //所以判断是否创建代理对象 都在实例化之前的放入三级缓存时就判断了 并且记录下来了
            //在后续的初始化方法里的处理器方法里会不代理 那么返回的bean就是普通bean
            //所以需要从二级缓存中取出来代理bean，再放入一级缓存
            Object exposedObject = getSingleton(beanName);
            addSingleton(beanName, exposedObject);
        }


        return bean;
    }


    /**
     * 判断是返回普通bean 还是代理bean
     * @param beanName
     * @param beanDefinition
     * @param bean
     * @return
     */
    protected Object getEarlyBeanReference(String beanName, BeanDefinition beanDefinition, Object bean) {
        Object exposedObject = bean;
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                //里面封装了判断获取普通bean还是代理bean的逻辑
                exposedObject = ((InstantiationAwareBeanPostProcessor) bp).getEarlyBeanReference(exposedObject, beanName);
                if (exposedObject == null) {
                    return exposedObject;
                }
            }
        }

        return exposedObject;
    }


    /**
     * bean实例化后执行，如果返回false，不执行后续设置属性的逻辑
     * 这里写死返回值为true了啊 那一定会走后续赋值
     *
     * @param beanName
     * @param bean
     * @return
     */
    private boolean applyBeanPostProcessorsAfterInstantiation(String beanName, Object bean) {
        boolean continueWithPropertyPopulation = true;
        for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
            if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
                if (!((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessAfterInstantiation(bean, beanName)) {
                    continueWithPropertyPopulation = false;
                    break;
                }
            }
        }
        return continueWithPropertyPopulation;
    }

    /**
     * 在设置bean属性之前，允许BeanPostProcessor修改属性值 实际上赋值到bean对象里了已经
     *
     * @param beanName
     * @param bean
     * @param beanDefinition
     */
    protected void applyBeanPostProcessorsBeforeApplyingPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
        for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
            if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {

                //这里的逻辑 已经实现了将@Value注解设置的属性值 通过反射赋值到bean里了
                //并且应该返回了 完整的属性值集合 这里需要添加到bean定义对象里
                PropertyValues pvs = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessPropertyValues(
                        beanDefinition.getPropertyValues(), bean, beanName
                );

                if (pvs != null) {
                    for (PropertyValue propertyValue : pvs.getPropertyValues()) {
                        //重复的key添加 则是修改 新的key 则是添加
                        beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
                    }
                }

            }
        }
    }


    /**
     * 注册有销毁方法的bean，即bean继承自DisposableBean或有自定义的销毁方法
     * 把有销毁方法的bean都封装到DisposableBeanAdapter统一做好处理了 不论是接口还是指定了方法的
     *
     * @param beanName
     * @param bean
     * @param beanDefinition
     */
    protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
        //只有singleton类型bean会执行销毁方法
        if (beanDefinition.isSingleton()) {
            //说明有销毁方法 需要注册到销毁bean列表里
            if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
                //把有销毁方法的bean都封装到DisposableBeanAdapter统一做好处理了 不论是接口还是指定了方法的
                registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
            }
        }

    }

    /**
     * 通过反射给实例化的bean添加属性
     *
     * @param beanName
     * @param bean
     * @param beanDefinition
     */
    private void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
        try {
            //beanDefinition里有需要添加的属性值
            for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()) {
                String name = propertyValue.getName();
                Object value = propertyValue.getValue();

                //通过反射获取set方法赋值
//                Class<?> type = beanClass.getDeclaredField(name).getType();
//                String methodName = "set" +name.substring(0, 1).toUpperCase() + name.substring(1);
//                Method method = beanClass.getDeclaredMethod(methodName, new Class[]{type});
//                method.invoke(bean, new Object[]{value});
                //判断注入的属性是否是bean对象--防止循环依赖吧。。
                if (value instanceof BeanReference) {
                    BeanReference beanReference = (BeanReference) value;
                    // beanA依赖beanB，先实例化beanB
                    //将获取的bean直接赋值给value 以免下面的代码修改
                    value = getBean(beanReference.getBeanName());
                } else {
                    //执行类型转换
                    //获取value的类型
                    Class<?> sourceType = value.getClass();

                    //获取bean里这个属性的真实的类型
                    Class<?> targetType = (Class<?>) TypeUtil.getFieldType(bean.getClass(), name);

                    //转换为真实的目标类型
                    ConversionService conversionService = getConversionService();
                    if (conversionService != null){
                        //可以转换就转换
                        if(conversionService.canConvert(sourceType, targetType)){
                            value = conversionService.convert(value, targetType);
                        }
                    }
                }
                //hutool工具包实现类反射赋值
                BeanUtil.setFieldValue(bean, name, value);
            }
        } catch (Exception ex) {
            throw new BeansException("Error setting property values for bean: " + beanName, ex);
        }
    }

    /**
     * 初始化bean时前后执行扩展方法中间执行初始化方法的封装方法
     *
     * @param beanName
     * @param bean
     * @param beanDefinition
     */
    protected Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
        //在BeanFactory初始化bean的时候 给其赋值所属的BeanFactory
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(this);
        }

        //执行BeanPostProcessor的前置处理
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

        try {
            invokeInitMethods(beanName, wrappedBean, beanDefinition);
        } catch (Throwable ex) {
            throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", ex);
        }

        //执行BeanPostProcessor的后置处理
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

        return wrappedBean;
    }

    /**
     * 执行在初始化之前的bean扩展方法
     *
     * @param existingBean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        //遍历所有的处理器 进行扩展
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessBeforeInitialization(result, beanName);
            if (current == null) {
                //TODO 为什么bean都为null了还要继续。。
                continue;
            }
            result = current;
        }

        return result;
    }

    /**
     * 执行在初始化之后的bean扩展方法
     *
     * @param existingBean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        //遍历所有的处理器 进行扩展
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                continue;
            }
            result = current;
        }

        return result;

    }

    /**
     * 执行bean的初始化方法
     *
     * @param beanName
     * @param bean
     * @param beanDefinition
     */
    protected void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Throwable {
        //如果当前bean实现了初始化接口则执行初始化方法
        if (bean instanceof InitializingBean) {
            ((InitializingBean) bean).afterPropertiesSet();
        }

        //如果有指定初始化方法则反射执行初始化方法
        String initMethodName = beanDefinition.getInitMethodName();
        //避免继承自InitializingBean,且自定义方法与InitializingBean方法同名，初始化方法执行两次的情况
        if (StrUtil.isNotEmpty(initMethodName) && !(bean instanceof InitializingBean && "afterPropertiesSet".equals(initMethodName))) {
            Method initMethod = ClassUtil.getPublicMethod(beanDefinition.getBeanClass(), initMethodName);
            if (initMethod == null) {
                throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
            }
            initMethod.invoke(bean);
        }
    }

    private Object createBeanInstance(BeanDefinition beanDefinition) {
        //通过bean的实例化策略来创建bean
        return getInstantiationStrategy().instantiate(beanDefinition);
    }

    public InstantiationStrategy getInstantiationStrategy() {
        return instantiationStrategy;
    }

    public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
        this.instantiationStrategy = instantiationStrategy;
    }


}
